package org.xtext.runtime.formula;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.term.ITerm;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.type.Type;
import org.xtext.runtime.type.TypeException;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.ILogicVisitor;


public class GreaterThan implements IFormula, IComparison {
	private ITerm left;
	private ITerm right;
	private Set<Variable> variables = new HashSet<Variable>();
	
	public GreaterThan(ITerm left, ITerm right) throws TypeException {
		this.left = left;
		this.right = right;

		if (!Type.isNumericType(left) || !Type.isNumericType(right)) {
			throw new TypeException("Greater than '>' only allows numeric values");
		}
		
		if (left instanceof Variable) {
			variables.add((Variable) left);
		}
		
		if (right instanceof Variable) {
			variables.add((Variable) right);
		}
	}
	
	@Override
	public GreaterThan copy() {
		try {
			return new GreaterThan(left.copy(), right.copy());
		} catch (TypeException e) {
			return null;
		}
	}

	@Override
	public Set<Variable> variables() {
		return variables;
	}

	@Override
	public GreaterThan apply(Bindings bindings) {
		for (Variable variable : variables) {
			if (variable.isFree()) {
				try {
					ITerm value = bindings.getStoredValue(variable);
					if (value != null) variable.assign(value);
				} catch (TypeException e) {
					return null;
				}
			}
		}
		return this;
	}

	@Override
	public GreaterThan copy(Map<String, Variable> vars) {
		try {
			return new GreaterThan(
					left.copy(vars),
					right.copy(vars)
			);
		} catch (TypeException e) {
			return null;
		}
	}

	@Override
	public boolean evaluate() {
		ITerm l = Variable.rawValue(left);
		ITerm r = Variable.rawValue(right);
		
		try {
			if (l.type().equals(Type.integerType)) {
				return Type.termAsInt(l) > Type.termAsInt(r);
			} else if (l.type().equals(Type.longType)) {
				return Type.termAsLong(l) > Type.termAsLong(r);
			} else if (l.type().equals(Type.floatType)) {
				return Type.termAsFloat(l) > Type.termAsFloat(r);
			} else if (l.type().equals(Type.doubleType)) {
				return Type.termAsDouble(l) > Type.termAsDouble(r);
			}
		} catch (TypeException e) {
			e.printStackTrace();
			return false;
		}
		System.err.println("WARNING: Attempt to evaluate failed: " + this);
		return false;
	}
	
	public String toString() {
		return left + ">" + right;
	}

	@Override
	public boolean hasFreeVariables() {
		return left.hasFreeVariables() || right.hasFreeVariables();
	}

	@Override
	public Object accept(ILogicVisitor visitor) {
		return visitor.visit(this);
	}

	public ITerm left() {
		return left;
	}

	public ITerm right() {
		return right;
	}
}